//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2004
//
// Stuff for 'Edit|Undo' and 'Edit|Redo' commands.
//

# include "StdAfx.h"
# include "UndoRedo.h"
# include "Menu.h"
# include "MapView.h"
# include "Position.h"
# include "BackupAction.h"
# include "CombinedAction.h"
# include "SaveState.h"
# include "Globals.h"

bool g_bModified = false;

actions_t g_Undo;
actions_t g_Redo;

bool CanUndo () {return ! g_Undo.empty ();}
bool CanRedo () {return ! g_Redo.empty ();}

void ResetUndoRedo () {
	actions_t::iterator i;
	for (i = g_Undo.begin (); i != g_Undo.end (); ++ i)
		delete * i;
	g_Undo.clear ();

	for (i = g_Redo.begin (); i != g_Redo.end (); ++ i)
		delete * i;
	g_Redo.clear ();
}

bool PerformEditAction (CEditAction * _pAction) {
	assert (_pAction);

	CEditAction::cu_t cu = _pAction->CanUndo ();
	if (cu == CEditAction::cuBackup) {
		// Estimate free memory.
		bool bHaveMemory = false;
		{
			CCritSec::CLock lock (g_cs);
			const size_t cMapOccupied = g_map.occupied ();

			MEMORYSTATUS ms = {sizeof (ms)};
			::GlobalMemoryStatus (& ms);
			const size_t cFree = ms.dwAvailPhys;

			bHaveMemory = cFree > cMapOccupied;
		}
		if (bHaveMemory) {
			cu = CEditAction::cuYes;
			_pAction = new CBackupAction (_pAction);
		} else
			cu = CEditAction::cuNo;
	}
	if (cu == CEditAction::cuNo && ! g_Undo.empty ()) {
		string_t strName;
		_pAction->GetName (strName);

		if (
			YesNoMsgBox (
				"Please be aware that the action cannot be undone later:\n"
				" '%s'.\n"
				"\n"
				"Do you want to continue?\n"
				"(Press YES to apply the action or NO to cancel it.)",
				strName.c_str ()
			) == false
		) {
			delete _pAction;
			return false;
		}
	}

	if (! _pAction->Do ()) {
		string_t strName;
		_pAction->GetName (strName);

		ErrorMsgBox ("The action failed:\n %s.", strName.c_str ());

		delete _pAction;
		return false;
	}

	const bool bWasModified = g_bModified;
	g_bModified = true;

	if (cu == CEditAction::cuYes) {
		// NOTE: action may modify selection in Do().
		if (cu == CEditAction::cuYes && ! IsInCombinedAction ())
			_pAction = new CSaveStateWrapperAction (_pAction, g_Selection, bWasModified);

		// Reset Redo stack.
		for (actions_t::iterator i = g_Redo.begin (); i != g_Redo.end (); ++ i)
			delete * i;
		g_Redo.clear ();

		g_Undo.push_back (_pAction);
	} else {
		ResetUndoRedo ();
		delete _pAction;
	}

	if (! IsInCombinedAction ())
		UpdateMenu ();

	return true;
}

void UndoEditAction () {
	if (g_Undo.empty ())
		return;

	CEditAction * const pAction = g_Undo.back ();
	g_Undo.pop_back ();

	pAction->Undo ();

	g_Redo.push_back (pAction);

	// TODO: ? restore the flag ?
//	g_bModified = true;
}

void RedoEditAction () {
	if (g_Redo.empty ())
		return;

	CEditAction * const pAction = g_Redo.back ();
	g_Redo.pop_back ();

	pAction->Redo ();

	g_Undo.push_back (pAction);

	g_bModified = true;
}

